package com.scalefuture.gmemcached;

import com.sun.grizzly.ProtocolParser;

import java.nio.ByteBuffer;

/**
 * Memcached Protocol Parser
 *
 * @author <a href="mailto:dev@scalefuture.com">Steve Tzou</a>
 */
public class MemcachedProtocolParser implements ProtocolParser {
    ByteBuffer savedBuffer;
    byte[] data;
    int position;
    int origLimit;
    int limit;

    private boolean partial = false;
    private Object message;

    public boolean isExpectingMoreData() {
        return partial;
    }

    public boolean hasMoreBytesToParse() {
        return position < limit;
    }

    public Object getNextMessage() {
        return message;
    }

    public boolean hasNextMessage() {
        String s = new String(data, position, limit - position);
        int cmdline = s.indexOf("\r\n");
        if (cmdline == -1 || s.replaceAll("\r\n", "").length() == 0) {
            partial = true;
        } else {
            String[] c = s.substring(0, cmdline).split(" ", 6);
            if (c[0].equalsIgnoreCase("add") || c[0].equalsIgnoreCase("set") || c[0].equalsIgnoreCase("replace")) {
                if (c.length < 5) {
                    partial = false;
                    message = GMemcached.CLIENT_ERROR.replaceAll("#", "Parameters format error");
                    position += s.substring(0, "\r\n".length() + cmdline).getBytes().length;
                } else {
                    int dataByteSize = Integer.parseInt(c[4]);
                    dataByteSize = dataByteSize + "\r\n".getBytes().length;
                    if (s.substring(0, "\r\n".length() + cmdline).getBytes().length + dataByteSize <= s.getBytes().length) {
                        partial = false;
                        Command command = new Command();
                        command.setCmd(c[0]);
                        command.setKey(c[1]);
                        command.setFlags(c[2]);
                        command.setExpire(Integer.parseInt(c[3]));
                        command.setData_length(Integer.parseInt(c[4]));

                        command.setData(new String(data, position + s.substring(0, "\r\n".length() + cmdline).getBytes().length, dataByteSize - "\r\n".getBytes().length).getBytes());

                        position += s.substring(0, "\r\n".length() + cmdline).getBytes().length + dataByteSize;
                        command.setNoreplay(c.length == 6 && c[6].equalsIgnoreCase("noreply"));
                        message = command;
                    } else {
                        partial = true;
                    }
                }
            } else
            if (c[0].equalsIgnoreCase("get") || c[0].equalsIgnoreCase("gets") || c[0].equalsIgnoreCase("incr") || c[0].equalsIgnoreCase("decr") || c[0].equalsIgnoreCase("delete") || c[0].equalsIgnoreCase("verbosity") || c[0].equalsIgnoreCase("stats")) {
                if (c.length > 1) {
                    partial = false;
                    Command command = new Command();
                    command.setCmd(c[0]);
                    for (int i = 1; i < c.length; i++) {
                        command.addArgs(c[i]);
                    }
                    message = command;
                } else {
                    partial = false;
                    message = GMemcached.CLIENT_ERROR.replaceAll("#", "Parameters format error");
                }
                position += s.substring(0, "\r\n".length() + cmdline).getBytes().length;
            } else
            if (c[0].equalsIgnoreCase("flush_all") || c[0].equalsIgnoreCase("quit") || c[0].equalsIgnoreCase("version")) {
                partial = false;
                Command command = new Command();
                command.setCmd(c[0]);
                message = command;
                position += s.substring(0, "\r\n".length() + cmdline).getBytes().length;
            } else {
                partial = false;
                message = GMemcached.ERROR;
                position += s.substring(0, "\r\n".length() + cmdline).getBytes().length;
            }
        }
        return !partial;
    }

//    private void setSTAT(String key,Object value) {
//
//    }

    public void startBuffer(ByteBuffer byteBuffer) {
        savedBuffer = byteBuffer;
        savedBuffer.flip();
        partial = false;
        origLimit = savedBuffer.limit();

        if (savedBuffer.hasArray()) {
            data = savedBuffer.array();
            position = savedBuffer.position() + savedBuffer.arrayOffset();
            limit = savedBuffer.limit() + savedBuffer.arrayOffset();
        }
    }

    public boolean releaseBuffer() {
        if (!hasMoreBytesToParse()) {
            savedBuffer.clear();
        } else {
            savedBuffer.position(position);
            savedBuffer.limit(origLimit);
        }
        return partial;
    }
}
